﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using TccAg.VO;
 

namespace TccAg.Classes.Core
{
    class Gerador
    {
        private CursoVO curso;
        private List<GradeAcademicaVO> populacao;
        private List<GradeAcademicaVO> novaPopulacao;
        private PeriodoacademicoVO periodo;

        public PeriodoacademicoVO Periodo
        {
            get { return periodo; }
            set { periodo = value; }
        }

        public CursoVO Curso
        {
            get { return curso; }
            set { curso = value; }
        }

        public List<GradeAcademicaVO> Populacao
        {
            get { return populacao; }
            set { populacao = value; }
        }

        public void gerarGrades()
        {
            GradeAcademicaVO grd1 = new GradeAcademicaVO();
            GradeAcademicaVO grd2 = new GradeAcademicaVO();
            //Verifico se o período está configurado
            //verifico se os parâmetros estão configurados
            //verifico se a população está gerada e se á a primeira geração
            //Aplico o elitismo
            aplicarElitismo();
            //Seleciono dois indivíduos            
            grd1 = selecionarPorTorneio();
            grd2 = selecionarPorTorneio();
            //processo o cruzamento
            cruzamentoDoisPontos(grd1, grd2);
            //processo a mutação
            //avalio
        }

        private GradeAcademicaVO selecionarPorTorneio()
        {
            GradeAcademicaVO grd1 = new GradeAcademicaVO();
            GradeAcademicaVO grd2 = new GradeAcademicaVO();
            int maxRange = populacao.Count;
            int index = 0;

            index = Utils.Util.randomId(0, maxRange);
            grd1 = populacao[index];
            index = Utils.Util.randomId(0, maxRange);
            grd2 = populacao[index];

            if (grd1.Nota > grd2.Nota)
                return grd1;
            else
                return grd2;

        }

        public double avaliarCompleto(GradeAcademicaVO grade)
        {
            return 0;
        }

        private List<GradeAcademicaVO> cruzamentoUmPonto(GradeAcademicaVO grd1, GradeAcademicaVO grd2)
        {
            List<GradeAcademicaVO> retorno = new List<GradeAcademicaVO>();
            GradeAcademicaVO filho1 = new GradeAcademicaVO();
            GradeAcademicaVO filho2 = new GradeAcademicaVO();
            
            GradeAcademicaVO pai = new GradeAcademicaVO();
            GradeAcademicaVO mae = new GradeAcademicaVO();

            int sort = Utils.Util.randomId(1, 10);
            if (sort % 2 == 0)
            {
                pai = grd1;
                mae = grd2;
            }
            else
            {
                pai = grd2;
                mae = grd1;
            }

            int maxRange = grd1.Elementos.Count - 2;
            int pontoDeCorte = Utils.Util.randomId(1, maxRange);

            for (int i = 0; i < pai.Elementos.Count; i++)
            {
                if ((i + 1) <= pontoDeCorte)
                {
                    filho1.Elementos.Add(pai.Elementos[i]);
                    filho2.Elementos.Add(mae.Elementos[i]);
                }
                else
                {
                    filho1.Elementos.Add(mae.Elementos[i]);
                    filho2.Elementos.Add(pai.Elementos[i]);
                }
            }
            filho1.Geracao = pai.Geracao + 1;
            filho2.Geracao = pai.Geracao + 1;
            filho1.Nota = avaliarCompleto(filho1);
            filho2.Nota = avaliarCompleto(filho2);
            retorno.Add(filho1);
            retorno.Add(filho2);

            return retorno;

        }

        private List<GradeAcademicaVO> cruzamentoDoisPontos(GradeAcademicaVO grd1, GradeAcademicaVO grd2)
        {
            List<GradeAcademicaVO> retorno = new List<GradeAcademicaVO>();
            GradeAcademicaVO filho1 = new GradeAcademicaVO();
            GradeAcademicaVO filho2 = new GradeAcademicaVO();

            GradeAcademicaVO pai = new GradeAcademicaVO();
            GradeAcademicaVO mae = new GradeAcademicaVO();

            int sort = Utils.Util.randomId(1, 10);
            if (sort % 2 == 0)
            {
                pai = grd1;
                mae = grd2;
            }
            else
            {
                pai = grd2;
                mae = grd1;
            }

            int maxRange = grd1.Elementos.Count - 2;
            int pontoDeCorte1 = 0;
            int pontoDeCorte2 = 0;
            while (pontoDeCorte1 == pontoDeCorte2)
            {
                pontoDeCorte1 = Utils.Util.randomId(1, maxRange);
                pontoDeCorte2 = Utils.Util.randomId(1, maxRange);
            }
            int aux = pontoDeCorte1;
            if (pontoDeCorte1 > pontoDeCorte2)
            {
                pontoDeCorte1 = pontoDeCorte2;
                pontoDeCorte2 = aux;
            }

            for (int i = 0; i < pai.Elementos.Count; i++)
            {
                if ((pontoDeCorte1 < (i + 1)) && ((i + 1) <= pontoDeCorte2))
                {
                    filho1.Elementos.Add(pai.Elementos[i]);
                    filho2.Elementos.Add(mae.Elementos[i]);
                }
                else
                {
                    filho1.Elementos.Add(mae.Elementos[i]);
                    filho2.Elementos.Add(pai.Elementos[i]);
                }
            }
            filho1.Geracao = pai.Geracao + 1;
            filho2.Geracao = pai.Geracao + 1;
            filho1.Nota = avaliarCompleto(filho1);
            filho2.Nota = avaliarCompleto(filho2);
            retorno.Add(filho1);
            retorno.Add(filho2);

            return retorno;

        }
        
        private void ordenarSemestres(GradeAcademicaVO grade)
        {
            List<SemestreVO> tempsem = new List<SemestreVO>();
            var qrySemestre = from a in grade.Curso.Semestres
                              orderby a.Sequencia ascending
                              select a;
            foreach (var semestre in qrySemestre)
            {
                tempsem.Add(semestre);
            }
            grade.Curso.Semestres = tempsem;
        }

        private void ordenarPopulacao()
        {
            List<GradeAcademicaVO> tempGrade = new List<GradeAcademicaVO>();
            var qryPopulacao = from a in populacao
                              orderby a.Nota
                              select a;
            foreach (var individuo in qryPopulacao)
            {
                tempGrade.Add(individuo);
            }
           // grade.Curso.Semestres = tempGrade;
        }

        private void aplicarElitismo()
        {
            // Pego a quantidade de indivíduos a serem transferidos
            // Este valor deverá estar num parâmetro futuramente (Taxa de elitismo)
            int qtdeIndividuos = (int)Periodo.Parametros.Tampopulacao / 10;

            // Extraio os n melhores
            ordenarPopulacao();
            var qryElite = from grade in populacao.Take(qtdeIndividuos)
                           orderby grade.Nota descending
                           select grade;
            foreach (var g in qryElite)
            {
                g.Geracao++;
                novaPopulacao.Add(g);
            }
        }
    }
}
